use std::{rc::Rc, cell::RefCell};

use crate::{instructions::base::Instruction, rtda::Frame};


// 按索引取数组元素值，然后推入操作数栈。
#[derive(Debug, Default)]
pub struct AALOAD {}

impl Instruction for AALOAD {
    fn execute(&mut self, frame: Rc<RefCell<Frame>>) {
        // 操作数栈
        let stack = frame.borrow().get_operand_stack();
        // 弹出第一个操作数：数组索引
        let index = stack.borrow_mut().pop_int();
        // 弹出第二个操作数：数组引用
        let arr_ref = stack.borrow_mut().pop_ref().expect("java.lang.NullPointerException");

        let refs = unsafe { &mut *arr_ref }.array().refs();

        check_index(index, refs.len() as i32);
        stack.borrow_mut().push_ref(refs[index as usize].clone());
    }
}

#[derive(Debug, Default)]
pub struct BALOAD {}

impl Instruction for BALOAD {
    fn execute(&mut self, frame: Rc<RefCell<Frame>>) {
        let stack = frame.borrow().get_operand_stack();
        let index = stack.borrow_mut().pop_int();
        let arr_ref = stack.borrow_mut().pop_ref().expect("java.lang.NullPointerException");
        // let mut ref_mut = arr_ref.borrow_mut();
        let bytes = unsafe { &mut *arr_ref }.array().bytes();

        check_index(index, bytes.len() as i32);
        stack.borrow_mut().push_int(bytes[index as usize] as i32);
    }
}

#[derive(Debug, Default)]
pub struct CALOAD {}

impl Instruction for CALOAD {
    fn execute(&mut self, frame: Rc<RefCell<Frame>>) {
        let stack = frame.borrow().get_operand_stack();
        let index = stack.borrow_mut().pop_int();
        let arr_ref = stack.borrow_mut().pop_ref().expect("java.lang.NullPointerException");
        // let mut ref_mut = arr_ref.borrow_mut();
        let chars = unsafe { &mut *arr_ref }.array().chars();

        check_index(index, chars.len() as i32);
        stack.borrow_mut().push_int(chars[index as usize] as i32);
    }
}

#[derive(Debug, Default)]
pub struct DALOAD {}

impl Instruction for DALOAD {
    fn execute(&mut self, frame: Rc<RefCell<Frame>>) {
        let stack = frame.borrow().get_operand_stack();
        let index = stack.borrow_mut().pop_int();
        let arr_ref = stack.borrow_mut().pop_ref().expect("java.lang.NullPointerException");
        // let mut ref_mut = arr_ref.borrow_mut();
        let doubles = unsafe { &mut *arr_ref }.array().doubles();

        check_index(index, doubles.len() as i32);
        stack.borrow_mut().push_double(doubles[index as usize]);
    }
}

#[derive(Debug, Default)]
pub struct FALOAD {}

impl Instruction for FALOAD {
    fn execute(&mut self, frame: Rc<RefCell<Frame>>) {
        let stack = frame.borrow().get_operand_stack();
        let index = stack.borrow_mut().pop_int();
        let arr_ref = stack.borrow_mut().pop_ref().expect("java.lang.NullPointerException");
        // let mut ref_mut = arr_ref.borrow_mut();
        let floats = unsafe { &mut *arr_ref }.array().floats();

        check_index(index, floats.len() as i32);
        stack.borrow_mut().push_float(floats[index as usize]);
    }
}

#[derive(Debug, Default)]
pub struct IALOAD {}

impl Instruction for IALOAD {
    fn execute(&mut self, frame: Rc<RefCell<Frame>>) {
        let stack = frame.borrow().get_operand_stack();
        let index = stack.borrow_mut().pop_int();
        let arr_ref = stack.borrow_mut().pop_ref().expect("java.lang.NullPointerException");
        // let mut ref_mut = arr_ref.borrow_mut();
        let ints = unsafe { &mut *arr_ref }.array().ints();

        check_index(index, ints.len() as i32);
        stack.borrow_mut().push_int(ints[index as usize]);
    }
}

#[derive(Debug, Default)]
pub struct LALOAD {}

impl Instruction for LALOAD {
    fn execute(&mut self, frame: Rc<RefCell<Frame>>) {
        let stack = frame.borrow().get_operand_stack();
        let index = stack.borrow_mut().pop_int();
        let arr_ref = stack.borrow_mut().pop_ref().expect("java.lang.NullPointerException");
        // let mut ref_mut = arr_ref.borrow_mut();
        let longs = unsafe { &mut *arr_ref }.array().longs();

        check_index(index, longs.len() as i32);
        stack.borrow_mut().push_long(longs[index as usize]);
    }
}

#[derive(Debug, Default)]
pub struct SALOAD {}

impl Instruction for SALOAD {
    fn execute(&mut self, frame: Rc<RefCell<Frame>>) {
        let stack = frame.borrow().get_operand_stack();
        let index = stack.borrow_mut().pop_int();
        let arr_ref = stack.borrow_mut().pop_ref().expect("java.lang.NullPointerException");
        // let mut ref_mut = arr_ref.borrow_mut();
        let shorts = unsafe { &mut *arr_ref }.array().shorts();

        check_index(index, shorts.len() as i32);
        stack.borrow_mut().push_int(shorts[index as usize] as i32);
    }
}

// 用expect直接抛出异常，不清楚后续处理异常是否用到此处，暂不删除。
// fn check_not_none(arr_ref: &Option<Rc<RefCell<Object>>>) {
//     if arr_ref.is_none() {
//         panic!("java.lang.NullPointerException");
//     }
// }

fn check_index(index: i32, len: i32) {
    // IndexOutOffBounds
    if index < 0 || index >= len {
        panic!("ArrayIndexOutOffBoundsException")
    }
}

